import Expr from "./O.format";
import Ele from "../element";
import Abs from "../abyss";
import Io from './O.data.io';
import Dev from "../develop";

/**
 * ## 「引擎」`Ux.valueExpr`
 *
 * （通常内部）解析表达式强化方法，主要用于解析Zero中带`:`的自定义表达式相关信息。
 *
 * ```js
 * const user = {
 *     username: "Lang",
 *     age: 12
 * };
 *
 * const keepValue = Ux.valueExpr("你的名字:username", user, true);
 * // 输出：你的名字Lang。
 * // 执行后 user 的值为：{ username: "Lang", age: 12 };
 *
 * const removeValue = Ux.valueExpr("你的名字:username", user);
 * // 输出：你的名字Lang。
 * // 之后后 user 的值：{ age:12 }; 原始的 username 已经从输入Object中移除。
 * ```
 *
 * 注意第三参数，第三参数会对传入参数执行保留或不保留的两种方案，如果 keep = false（默认），那么在执行过后，原始入参中
 * 被替换的参数属性会被移除掉，而 keep = true 时则不会移除，会保留其数据信息，并且注意该方法和`Ux.formatExpr`的区别，
 * 通常`valueExpr`是直接使用，建议在内部调用，而`formatExpr`会有更多操作，所以提供给用户调用。
 *
 * @memberOf module:_value
 * @param {String} expr 传入的表达式或字段名。
 * @param {Object} data 记录信息。
 * @param {boolean} keep 格式化过后是否保留原始的值的。
 * @return {string} 返回最终格式化后的数据。
 */
const valueExpr = (expr = "", data = {}, keep = false) => {
    let display;
    if (0 <= expr.indexOf(":")) {
        display = Expr.formatExpr(expr, data, keep);
    } else {
        display = data[expr];
        if (!display) display = "";
    }
    return display;
};
/**
 * ## 「引擎」`Ux.valueData`
 *
 * 专用解析工作流处理中的 data 节点信息，data 的数据格式：
 *
 * 1. 字符串："k=v:t,k=v:t"
 * 2. 数组类型：[]
 * 3. 对象类型：{} -> 直接处理
 *
 * 唯一的区别是对象和数组中都支持 `dataIngest` 方法对数据源的处理，此处会借用reference
 *
 * @memberOf module:_value
 * @param {Object} params 值解析处理
 * @param {String|Array|Object} data 值解析处理
 * @param reference
 * @return {Object} 返回解析后的对象信息
 */
const valueTran = (params = {}, data, reference) => {
    const normalized = {};
    if ("string" === typeof data) {
        // 格式1：不可能包含内置解析部分
        const pairs = data.split(",");
        pairs.forEach(expr => {
            const kv = expr.split("=");
            const field = kv[0];
            let value = kv[1];
            if (field && value) {
                const vt = value.split(":");
                if (1 === vt.length) {
                    // <VALUE>
                    value = vt[0];
                } else if (2 === vt.length) {
                    // <VALUE>:<TYPE>
                    value = Ele.valueT(vt[0], vt[1]);
                }
                normalized[field] = value;
            }
        })
    } else if (Abs.isArray(data)) {
        // 格式2：数组类型
        // -- 元素为字符串：   递归格式1
        // -- 元素为对象：     递归格式2
        // -- 元素为数组：     递归格式3
        data.forEach(each => {
            const element = valueTran(params, each, reference);
            Object.assign(normalized, element);
        })
    } else if (Abs.isObject(data)) {
        const {inSource} = data;
        if (inSource) {
            // 格式3：可解析
            // 可解析的格式必须包含 inSource 字段
            const {inField, ...inRest} = data;
            normalized[inField] = Io.dataIo(params, inRest, reference);
        } else {
            // 格式4：普通对象
            // 专用合并，无 inSource 模式
            // 即使无 inSource 模式，但在配置过程中依旧会执行内置解析
            const nested = {};
            Object.keys(data).forEach(field => {
                if (Abs.isObject(data[field])) {
                    const config = {
                        ...data[field],
                        inField: field,
                    };
                    const element = valueTran(params, config, reference);
                    Object.assign(normalized, element);
                } else {
                    const exprValue = data[field];
                    if ("string" === typeof exprValue && exprValue.startsWith("FORM")) {
                        const exprField = exprValue.split(":")[1];
                        nested[field] = params[exprField];
                    } else {
                        nested[field] = data[field];
                    }
                }
            })
            Object.assign(normalized, nested);
        }
    }
    return normalized;
}
/**
 * ## 「引擎」`Ux.valueIndicate`
 *
 * 专用解析工作流处理中带有`indicator`字段配置的数据格式
 *
 * @memberOf module:_value
 * @param {Object} params 值解析处理
 * @param {Object} config 配置信息
 * @param reference
 * @return {Object} 返回解析后的对象信息
 */
const valueIndicate = (params = {}, config = {}, reference) => {
    /*
     * indicator 有三种值格式：
     * 1）（无值）：不执行任何规则，直接对 data 执行赋值，最简单配置。
     * 2）字符串：执行单一判断规则，根据一个指示变量执行最终数据拼合。
     * 3）数组：执行多判断规则，底层规则会追加上层内容，如两个规则同时更改某一个字段值：
     * -- RULE1, phase1
     * -- RULE2, phase2（没有phase1字段时phase2才生效）
     */
    const {
        indicator,
        data,
    } = config;
    let nData = {};
    if (indicator) {
        if (Abs.isArray(indicator)) {
            const keyArray = [];
            indicator.forEach(keyF => {
                const key1 = Ele.valueT(params[keyF]);
                keyArray.push(key1);
            });
            const key = keyArray.join("/");
            const dataPart = data[key];
            if (dataPart) {
                // 计算后提取 data
                nData = valueTran(params, dataPart, reference);
            }
        } else if ("string" === typeof indicator) {
            const key = Ele.valueT(params[indicator]);
            const dataPart = data[key];
            if (dataPart) {
                // 计算后提取 data
                nData = valueTran(params, dataPart, reference);
            }
        } else {
            // 直接提取 data
            nData = valueTran(params, data, reference);
        }
    } else {
        // 直接提取 data 节点的值
        nData = valueTran(params, data, reference);
    }
    Dev.dgDebug(nData, "[NData] Indicator 翻译结果", "#0093ed");
    return nData;
}
/**
 * ## 「引擎」`Ux.valueFind`
 *
 * 配置解析统一调用函数（特殊应用）
 *
 * 1. 如果 attrPath 长度为1，则直接提取 target 中的：`target[attrPath[0]]`。
 * 2. 如果长度大于2，则先提取`target[attrPath[0]]`，其次提取第二阶段名称，暂时只支持长度为2的模式。
 *
 * 内部执行函数代码示例。
 *
 * ```js
 PROP: (expression, {props}) => fnPredicate("PROP", expression, () => {
     const path = expression.split('.'); // 路径解析
     return T.valueFind(props, path);
 }),
 * ```
 *
 * @memberOf module:_value
 * @param {any} target 被查找的引用信息。
 * @param {Array} attrPath 属性路径信息。
 * @return {any|*} 返回最终解析的属性值。
 */
const valueFind = (target = {}, attrPath = []) => {
    if (2 <= attrPath.length) {
        const targetKey = attrPath[0];
        const path = Abs.clone(attrPath);
        const name = path.splice(0, 1);
        if (targetKey && 0 < path.length) {
            return Ele.ambFind(target, `$${targetKey}`, path);
        } else {
            console.error(`[ Ux ] 解析的配置不对，key = $${targetKey}, name = ${name}`);
        }
    } else {
        if (1 === attrPath.length) {
            /*
             * 长度为1，直接提取
             */
            const targetKey = attrPath[0];
            return target[targetKey];
        } else {
            console.error(`[ Ux ] 解析表达式有问题，请检查：$${target}`);
        }
    }
};

const toFieldName = (cell) => {
    let field;
    if (Abs.isObject(cell)) {
        if (cell.metadata) {
            field = cell.metadata.split(',')[0]
        } else {
            field = cell.field;
        }
    } else {
        field = cell.split(',')[0];
    }
    return field;
}
/**
 * ## 「引擎」`Ux.toLimit`
 *
 * 功能和 `valueLimit`相同，同样的执行操作，用于不同的场景。
 *
 * * `valueLimit` 属于标准函数。
 * * `toLimit` 在自定义组件中调用。
 *
 * @memberOf module:_to
 * @param {Props} props React组件的属性值。
 * @param {Array} limits 需要限制的 keys 的值。
 * @return {Object} 返回限制过后的属性值。
 */
const toLimit = (props = {}, limits = []) => {
    const inherits = {};
    Object.keys(props)
        .filter(key => !limits.includes(key))
        .forEach(key => inherits[key] = props[key]);
    return inherits;
};
/**
 * ## 「引擎」`Ux.toForm`
 *
 * 将两个form的配置进行合并的专用操作，主要合并项：
 *
 * 1. ui：两个表单直接连接。
 * 2. hidden：两个表单的隐藏字段连接。
 * 3. initial：初始值二者合并。
 * 4. mapping：映射值二者合并。
 * 5. op：执行操作（包括操作权限）的合并。
 * 6. io：「新版」执行转换（输入转换、提交转换）的合并
 * 7. model：「新版」窗口专用
 * 8. assist：「新版」辅助数据专用
 *
 * @memberOf module:_to
 * @param {Object} staticForm 静态表单配置。
 * @param {Object} dynamicForm 动态表单配置。
 * @return {Object} 返回最终的表单配置。
 */
const toForm = (staticForm = {}, dynamicForm = {}) => {
    /*
     * form：输入的 form
     * dynamicForm：动态输入的 form
     * 1）执行 ui 的合并
     * 2）执行 hidden 的合并
     * 3）执行 initial 的合并
     * 4）执行 op 的合并
     * 5）执行 mapping 的合并
     * 6）执行 io 的合并
     * 7）执行 modal 的合并
     * 8）执行 assist 的合并
     *
     * 唯一不执行合并的是 as / segment
     * 最新版本中引入了 io 部分的合并，io包括：
     * 1）reader：读取数据时执行的转换，修改 $inited 数据
     * 2）writer：提交数据（验证之前）的转换，修改 request 数据
     */
    // 先执行拷贝
    const form = staticForm ? Abs.clone(staticForm) : {};
    /*
     * 动态中的属性优先
     */
    const {
        ui = [],
        hidden = [],
        initial = {},
        mapping = {},
        io = {},
        modal = {},
        assist = {},
        ...rest
    } = dynamicForm;
    if (!Abs.isEmpty(rest)) {
        Object.assign(form, rest);
    }
    /*
     * 合并 ui 项
     */
    if (Abs.isArray(ui) && 0 < ui.length) {
        if (!form.ui) form.ui = [];
        form.ui = [].concat(form.ui, ui);
    }
    if (Abs.isArray(hidden) && 0 < hidden.length) {
        if (!form.hidden) form.hidden = [];      // 防止原生未配置
        form.hidden = [].concat(form.hidden, hidden);
    }
    /*
     * - assist
     * - initial
     * - mapping
     * - io
     * - modal
     */
    if (Abs.isNotEmpty(assist)) {
        if (!form.assist) form.assist = {};
        Object.assign(form.assist, assist);
    }
    if (Abs.isNotEmpty(initial)) {
        if (!form.initial) form.initial = {};
        Object.assign(form.initial, initial);
    }
    if (Abs.isNotEmpty(mapping)) {
        if (!form.mapping) form.mapping = {};
        Object.assign(form.mapping, mapping);
    }
    if (Abs.isNotEmpty(io)) {
        if (!form.io) form.io = {};
        Object.assign(form.io, io);
    }
    if (Abs.isNotEmpty(modal)) {
        if (!form.modal) form.modal = {};
        // Assign 必须这样处理
        form.modal = Abs.assign(form.modal, modal, 2);
    }
    return form;
};
const toFormUi = (ui = [], segment = {}) => {
    // ui迭代
    if (Abs.isArray(ui)) {
        const uiAfter = [];
        ui.forEach(row => {
            const rowAfter = [];
            if (Abs.isArray(row)) {
                // row 必须也是数组
                row.forEach((cell, cellIndex) => {
                    // 查看 cell 是否 complex 类型
                    if (cell.complex) {
                        /*
                         *  特殊类型提取
                         *  {
                         *      "complex": true,
                         *      "config": {
                         *          "pages": {
                         *               "key": []
                         *          }
                         *      }
                         *  }
                         */
                        const cellAdd = Abs.clone(cell);
                        if (cellAdd.config && cellAdd.config['pages']) {
                            const {pages = {}} = cellAdd.config;
                            const pagesAdd = {};
                            Object.keys(pages).forEach(key => {
                                const pageForm = pages[key];
                                if (pageForm && Abs.isArray(pageForm.ui)) {
                                    /*
                                     * 「递归」复杂页面直接执行递归替换
                                     */
                                    pagesAdd[key] = Abs.clone(pageForm);
                                    pagesAdd[key].ui = toFormUi(pageForm.ui, segment);
                                }
                            });
                            cellAdd.config.pages = pagesAdd;
                        }
                        rowAfter.push(cellAdd);
                    } else {
                        if (Abs.isObject(cell)) {
                            /*
                             * Object单元格普通追加
                             * [
                             *     {},{},{}
                             * ]
                             */
                            rowAfter.push(cell);
                        } else if ("string" === typeof cell) {
                            const target = segment[cell];
                            if (Abs.isArray(target)) {
                                // Cell 列连接
                                target.filter(Abs.isArray).filter(Abs.isNotEmpty)
                                    // Ui 追加都要考虑不为空
                                    .forEach(each => uiAfter.push(each))
                            } else {
                                /*
                                 * String单元格普通追加
                                 * [
                                 *     "","",""
                                 * ]
                                 */
                                rowAfter.push(cell);
                            }
                        } else {
                            console.warn("列连接非法", cell);
                        }
                    }
                })
            } else if ("string" === typeof row) {
                // Row 行连接
                const target = segment[row];
                // 二维数组追加到一维数据
                if (Abs.isArray(target)) {
                    // Ui 追加都要考虑不为空
                    target.filter(Abs.isArray).filter(Abs.isNotEmpty)
                        .forEach(each => uiAfter.push(each))
                }
            }
            if (Abs.isNotEmpty(rowAfter)) {
                // Ui 追加都要考虑不为空
                uiAfter.push(rowAfter);
            }
        })
        return uiAfter;
    } else {
        console.error("数据格式非法", ui)
        return [];
    }
}

const itField = (form = {}, consumer) => {
    const {ui = []} = form;
    ui.forEach(item => item.forEach(cell => {
        if (cell.complex) {
            const {config = {}} = cell;
            const {pages = {}} = config;
            Object.keys(pages).forEach(key => {
                const pageConfig = pages[key];
                itField(pageConfig, consumer);
            })
        } else {
            let field = toFieldName(cell);
            if (Abs.isFunction(consumer)) {
                consumer(field, cell);
            }
        }
    }))
}
// eslint-disable-next-line import/no-anonymous-default-export
export default {
    toForm,
    toFormUi,
    toFieldName,
    toLimit,
    itField,
    valueExpr,
    valueFind,
    // 工作流新数据格式（可重用）
    valueTran,
    valueIndicate,
    // 同名函数
    elementChildTree: (array = [], current = {}, parentField = "parent") =>
        Ele.elementChildTree(array, current, parentField),
    elementChildren: (array = [], current = {}, parentField = "parent") =>
        Ele.elementChildren(array, current, parentField),
}